import {
  GaDbManager
} from './GaDbManager';
import {
  GaQueryOptions
} from './GaQueryOptions';
import {
  relationalStore
} from '@kit.ArkData';
import {
  _sInfo
} from '../01-LogManager/LogManager';

const FileName = "DatabaseEntity"

export enum DbAction {
  Select = "SELECT",
  From = "FROM",
  WHERE = "WHERE"
}


const PrimaryKeyName: string = "id";
const TableNameKey: string = "tableName"

export abstract class GaBaseBean {
  static tableName: string
  static items: [] = [];
  private static primaryKeyName: string = "id"
  abstract id: number | undefined;
  private static _dbManager?: GaDbManager

  //所有实例使用1个dbManger.
  static registerDbManager(dbManager: GaDbManager) {
    if (!this._dbManager) {
      this._dbManager = dbManager;
    }
  }

  get dbManager() {
    return GaBaseBean._dbManager;
  }

  //获取所有属性
  static getProperties(obj: any) {
    // let constructor = this as new()=> object;
    // let obj = new constructor();
    let props = Object.getOwnPropertyNames(obj);

  }

  static createTable(): Promise<void> {
    const conn = this._dbManager.getConnection();
    let sql = "CREATE TABLE IF NOT EXISTS tbKUnit (id INTEGER PRIMARY KEY AUTOINCREMENT, parentId INTEGER, content TEXT )"
    return conn.execute({ sql: sql })
  }


  /**
   * 1 insert
   */
  /**
   * 1.1 insert
   * @returns
   */
  async insert<T extends GaBaseBean>(): Promise<number> {
    let properties: string[] = [];
    let values: any[] = [];
    let placeholders: string[] = []
    // let b = proto["tableName"]
    let proto = Object.getPrototypeOf(this)
    let constr = proto.constructor;
    let tableName = constr[TableNameKey]
    this.getInsertPropertiesAndValues(properties, values, placeholders);
    // console.log(properties);
    if (!properties.length) {
      return -1;
    }
    let propertiesSeg = properties.join(", ")
    let placeholdersSeg = placeholders.join(", ")
    let sql = `INSERT INTO ${tableName} ( ${propertiesSeg} ) VALUES ( ${placeholdersSeg} );`;
    _sInfo(FileName, "insert", `sql is: ${sql}`);
    // console.log(values);
    let conn = this.dbManager.getConnection();
    let queryOption: GaQueryOptions = {
      sql: sql,
      values: values
    }
    await conn.execute(queryOption)
      .catch((e: Error) => {
        _sInfo(FileName, "insert", `insert error:${e.message}`);
        throw e;
      })
    _sInfo(FileName, "insert", "insert success");
    return 1;
  }

  /**
   * 2 delete
   */
  /**
   * 2.1 deleteById
   * @returns
   */
  // async delete<T extends DatabaseEntity>(): Promise<number> {
  //   let properties: string[] = [];
  //   let values: any[] = [];
  //   let placeholders: string[] = []
  //   // let b = proto["tableName"]
  //   let proto = Object.getPrototypeOf(this)
  //   let constr = proto.constructor;
  //   let tableName = constr[TableNameKey]
  //   this.getDeletePropertiesAndValues(properties, values, placeholders);
  //   console.log(properties);
  //   if (!properties.length) {
  //     return -1;
  //   }
  //   let conditions: string[] = [];
  //   for (let i = 0; i < properties.length; i++) {
  //     let p = properties[i];
  //     let conditionStr = `${p} = ?`
  //     conditions.push(conditionStr)
  //   }
  //   let conditionsSeg = conditions.join(" AND ")
  //   let whereSeg = conditions.length ? ` WHERE ${conditionsSeg}` : ""
  //   let sql = `DELETE FROM ${tableName} ${whereSeg} ;`;
  //   console.log(sql);
  //   console.log(values);
  //   let conn = this.dbManager.getConnection();
  //   return new Promise((resolve, reject) => {
  //     let queryOption: QueryOptions = {
  //       sql: sql,
  //       values: values
  //     }
  //     conn.query(queryOption, (err, results, fields) => {
  //       if (!err) {
  //         let affectedRows = results.affectedRows;
  //         console.log(`affectedRows: ${affectedRows}`);
  //         resolve(affectedRows);
  //       } else {
  //         console.log(err);
  //         reject();
  //       }
  //     });
  //   })
  // }

  static async deleteById<T extends GaBaseBean>(id: number): Promise<number> {
    let properties: string[] = [PrimaryKeyName];
    let values: any[] = [id];
    let tableName = this.tableName;
    let conditions: string[] = [];
    for (let i = 0; i < properties.length; i++) {
      let p = properties[i];
      let conditionStr = `${p} = ?`
      conditions.push(conditionStr)
    }
    let conditionsSeg = conditions.join(" AND ")
    let whereSeg = conditions.length ? ` WHERE ${conditionsSeg}` : ""
    let sql = `DELETE FROM ${tableName} ${whereSeg} ;`;
    _sInfo(FileName, "deleteById", `sql is: ${sql}`);
    // console.log(values);
    let conn = this._dbManager.getConnection();
    return new Promise((resolve, reject) => {
      let queryOption: GaQueryOptions = {
        sql: sql,
        values: values
      }
      // conn.query(queryOption, (err, results, fields) => {
      //   if (!err) {
      //     let affectedRows = results.affectedRows;
      //     console.log(`affectedRows: ${affectedRows}`);
      //     resolve(affectedRows);
      //   } else {
      //     console.log(err);
      //     reject();
      //   }
      // });
    })
  }


  /**
   * 3 update
   */
  /**
   * 3 update
   * 增量更新,
   * ignoreUndefined 是否忽略undefined, true,undefined/null的不操作    false,undefine/null 数据库里置为null
   * @returns
   */
  // async updateById<T extends DatabaseEntity>(ignoreUndefined: boolean = true): Promise<number> {
  //   let properties: string[] = [];
  //   let values: any[] = [];
  //   let placeholders: string[] = []
  //   // let b = proto["tableName"]
  //   let proto = Object.getPrototypeOf(this)
  //   let constr = proto.constructor;
  //   let tableName = constr[TableNameKey]
  //   this.getInsertPropertiesAndValues(properties, values, placeholders);
  //   console.log(properties);
  //   if (!properties.length) {
  //     return -1;
  //   }
  //   let updatePart = ""
  //   let wherePart = ""
  //   if (properties.length) {
  //     let updates: string[] = [];
  //     for (let i = 0; i < properties.length; i++) {
  //       let p = properties[i];
  //       let updateStr = `${p} = ?`
  //       updates.push(updateStr)
  //     }
  //     let updatesStr = updates.join(" , ");
  //     updatePart = `SET ${updatesStr}`
  //   }
  //   let idValue = this[PrimaryKeyName];
  //   wherePart = `WHERE ${PrimaryKeyName} = ?`
  //   values.push(idValue);
  //   let sql = `UPDATE ${tableName} ${updatePart} ${wherePart} ;`;
  //   console.log(sql);
  //   console.log(values);
  //   let conn = this.dbManager.getConnection();
  //   return new Promise((resolve, reject) => {
  //     let queryOption: GaQueryOptions = {
  //       sql: sql,
  //       values: values
  //     }
  //     conn.query(queryOption, (err, results, fields) => {
  //       if (!err) {
  //         let affectedRows = results.affectedRows;
  //         console.log(`affectedRows: ${affectedRows}`);
  //         resolve(affectedRows);
  //       } else {
  //         console.log(err);
  //         reject();
  //       }
  //     });
  //   })
  // }

  /**
   * 4 search
   */

  /**
   * 查所有的数据
   * @returns
   */
  static async search<T extends GaBaseBean>(pattern?: object): Promise<T[]> {
    let sql = `${DbAction.Select} id,parentId ${DbAction.From} ${this.tableName}`;
    let conn = this._dbManager.getConnection();
    let conditions: string[] = [];
    let args: any[] = [];
    if (pattern && Object.getOwnPropertyNames(pattern)) {
      let fields = Object.getOwnPropertyNames(pattern);
      for (const field of fields) {
        conditions.push(` ${field} = ? `);
        args.push(pattern[field])
      }
      let conditionsStr = conditions.join(' AND ');
      sql += ` ${DbAction.WHERE} ${conditionsStr}; `
    }
    _sInfo(FileName, "search", `sql is: ${sql}`);
    let options: GaQueryOptions = {
      sql: sql,
      values: args
    }
    let resultSet = await conn.query(options)
      .catch((e: Error) => {
        _sInfo(FileName, "search", `search error:${e.message}`);
        throw e;
      })
    // _sInfo(FileName, "search", `search suc`);
    _sInfo(FileName, "search", `row count: ${resultSet.rowCount}`);
    let results = this.parseResultSet<T>(resultSet);
    return results;
  }

  static async search2<T extends GaBaseBean>(pattern?: object): Promise<T[]> {
    let sql = `${DbAction.Select} * ${DbAction.From} ${this.tableName}`;
    let conn = this._dbManager.getConnection();
    let conditions: string[] = [];
    let args: any[] = [];
    if (pattern && Object.getOwnPropertyNames(pattern)) {
      let fields = Object.getOwnPropertyNames(pattern);
      for (const field of fields) {
        conditions.push(` ${field} = ? `);
        args.push(pattern[field])
      }
      let conditionsStr = conditions.join(' AND ');
      sql += ` ${DbAction.WHERE} ${conditionsStr}; `
    }
    _sInfo(FileName, "search", `sql is: ${sql}`);
    let options: GaQueryOptions = {
      sql: sql,
      values: args
    }
    let resultSet = await conn.query(options)
      .catch((e: Error) => {
        _sInfo(FileName, "search", `search error:${e.message}`);
        throw e;
      })
    // _sInfo(FileName, "search", `search suc`);
    _sInfo(FileName, "search", `row count: ${resultSet.rowCount}`);
    let results = this.parseResultSet<T>(resultSet);
    return results;
  }

  static parseResultSet<T>(resultSet: relationalStore.ResultSet): T[] {
    let results = [];
    // resultSet.columnCount;
    if (resultSet.goToFirstRow()) {
      let id = resultSet.getLong(0);
      let parentId = resultSet.getLong(1);
      // let content = resultSet.getString(2);
      let obj = { id, parentId }
      results.push(obj);
      while (resultSet.goToNextRow()) {
        let id = resultSet.getLong(0);
        let parentId = resultSet.getLong(1);
        // let content = resultSet.getString(2);
        let obj = { id, parentId }
        results.push(obj);
      }
    }
    return results;
  }


  /**
   * 使用数据库查询出来的属性,进行初始化
   * @param dbo
   */
  private initWithDbObject(dbo: any) {
    let props = Object.getOwnPropertyNames(dbo);
    if (props.length) {
      //动态为属性赋值
      for (let p of props) {
        this[p] = dbo[p];
      }
    }
  }

  /**
   * 外部传两个空数组,函数负责返回数据给这两个数组
   * @param properties
   * @param values
   */
  private getInsertPropertiesAndValues(properties: string[], values: any[], placeholders: string[]) {
    let props = Object.getOwnPropertyNames(this);
    if (props.length) {
      //动态为属性赋值
      for (let p of props) {
        console.log(`check prop : ${p}`)
        if (p == PrimaryKeyName) {
          continue
        }
        let value = this[p];
        if (value === undefined || value === null) {
          continue
        }
        properties.push(p);
        values.push(value)
        placeholders.push("?")
      }
    }
  }

  private getDeletePropertiesAndValues(properties: string[], values: any[], placeholders: string[]) {
    let props = Object.getOwnPropertyNames(this);
    if (props.length) {
      //动态为属性赋值
      for (let p of props) {
        console.log(`check prop : ${p}`)
        if (p == PrimaryKeyName) {
          properties.push(p);
          let value = this[p];
          values.push(value)
          placeholders.push("?")
          break;
        }
      }
    }
  }
}


/*
  let props = Object.getOwnPropertyNames(obj);
  可以获取实例变量的所有属性的列表.
  */

